package controller

import (
	"encoding/json"
	"fmt"
	"gitee.com/saxon134/sysmain/api"
	"gitee.com/saxon134/sysmain/conf"
	"gitee.com/saxon134/sysmain/db"
	"gitee.com/saxon134/sysmain/db/models"
	"gitee.com/saxon134/sysmain/errs"
	"github.com/saxon134/go-utils/saData"
	"github.com/saxon134/go-utils/saData/saHit"
	"github.com/saxon134/go-utils/saData/saTime"
	"github.com/saxon134/go-utils/saData/saUrl"
	"github.com/saxon134/go-utils/saHttp"
	"github.com/saxon134/go-utils/saLog"
	"net/http"
	"strings"
	"time"
)

func TaskRegisterHandler(w http.ResponseWriter, r *http.Request) {
	if checkSign(r) == false {
		api.ResError(w, errs.ErrorUnauthorized.Error())
		return
	}

	decoder := json.NewDecoder(r.Body)
	var params map[string]string
	_ = decoder.Decode(&params)
	if params["app"] == "" || params["host"] == "" || params["port"] == "" || params["keys"] == "" {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}

	var existedAry = make([]*models.TblTask, 0, 100)
	db.MySql.Table(models.TBNTask).Where("app = ?", params["app"]).Find(&existedAry)

	var keyAry = strings.Split(params["keys"], ",")
	var addAry = make([]*models.TblTask, 0, 20)
	var updateAry = make([]*models.TblTask, 0, 20)
	var appcase = params["host"] + ":" + params["port"]
	for _, key := range keyAry {
		if key == "" {
			continue
		}

		var existed *models.TblTask
		for _, e := range existedAry {
			if e.Key == key {
				existed = e
				break
			}
		}

		if existed == nil {
			//新增
			addAry = append(addAry, &models.TblTask{
				CreateAt:  saTime.Now(),
				App:       params["app"],
				Key:       key,
				Name:      "",
				Status:    1,
				Spec:      "",
				Registers: appcase,
				Runner:    "",
			})
		} else {
			//不存在则增加注册实例
			var registers = "," + existed.Registers + ","
			if strings.Contains(registers, ","+appcase+",") == false {
				if strings.HasSuffix(existed.Registers, ",") == false {
					existed.Registers += "," + appcase
				} else {
					existed.Registers += appcase
				}
				updateAry = append(updateAry, existed)
			}
		}
	}

	//已有任务，如果注册的时候没有则需要删除
	for _, e := range existedAry {
		var existed = false
		for _, key := range keyAry {
			if key == e.Key {
				existed = true
				break
			}
		}
		if existed == false {
			var registers = "," + e.Registers + ","
			if strings.Contains(registers, ","+appcase+",") == true {
				e.Registers = strings.ReplaceAll(registers, ","+appcase+",", ",")
				updateAry = append(updateAry, e)
			}
		}
	}

	if len(addAry) > 0 {
		err := db.MySql.Table(models.TBNTask).CreateInBatches(addAry, 50).Error
		if err != nil {
			saLog.Err(errs.New(err))
		}
	}

	if len(updateAry) > 0 {
		err := db.MySql.Table(models.TBNTask).Save(updateAry).Error
		if err != nil {
			saLog.Err(errs.New(err))
		}
	}

	api.ResSuccess(w, nil)
}

func TaskListHandler(w http.ResponseWriter, r *http.Request) {
	var query = r.URL.Query()
	var app = query.Get("app")

	var appcase = ""
	if query.Get("host") != "" && query.Get("port") != "" {
		appcase = query.Get("host") + ":" + query.Get("port")
	}

	//管理后台调用，返回任务完整信息，支持分页
	if appcase == "" {
		var name = query.Get("name")

		offset, limit := api.Paging(r)
		tx := db.MySql.Table(models.TBNTask)

		if app != "" {
			tx.Where("app = ?", app)
		}

		if name != "" {
			tx.Where(fmt.Sprintf("`key` like '%%%s%%' or name like '%%%s%%'", name, name))
		}

		if offset >= 0 && limit > 0 {
			tx.Offset(offset).Limit(limit)
		}

		var cnt int64
		err := tx.Count(&cnt).Error
		if db.MySql.IsError(err) {
			api.ResError(w, errs.New(err).Error())
			return
		}

		if cnt <= int64(offset) {
			api.ResSuccess(w, nil, cnt)
			return
		}

		var taskAry = make([]*models.TblTask, 0, saHit.Int(limit > 0, limit, 50))
		err = tx.Find(&taskAry).Error
		if db.MySql.IsError(err) {
			api.ResError(w, errs.New(err).Error())
			return
		}

		api.ResSuccess(w, taskAry, cnt)
		return
	} else
	//应用实例调用，获取可执行的任务，一次性返回所有，只返回key和spec
	{
		if app == "" {
			api.ResError(w, errs.ErrorParams.Error())
			return
		}

		var taskAry = make([]*models.TblTask, 0, 50)
		err := db.MySql.Table(models.TBNTask).Where("app = ?", app).Where("status = 2").Find(&taskAry).Error
		if db.MySql.IsError(err) {
			api.ResError(w, errs.New(err).Error())
			return
		}

		var result map[string]string
		for _, v := range taskAry {
			if v.Runner == appcase {
				result[v.Key] = v.Spec
			}
		}
		api.ResSuccess(w, result)
		return
	}
}

func TaskSaveHandler(w http.ResponseWriter, r *http.Request) {
	var obj = new(models.TblTask)
	decoder := json.NewDecoder(r.Body)
	_ = decoder.Decode(obj)
	if obj.Id <= 0 || obj.App == "" || obj.Key == "" {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}

	if obj.Runner != "" && obj.Spec == "" {
		api.ResError(w, "实例缺少执行时间")
		return
	}

	if obj.Spec != "" {
		var ary = strings.Split(obj.Spec, " ")
		if len(ary) != 5 && len(ary) != 6 {
			api.ResError(w, "执行时间格式有误")
			return
		}
	}

	var existed = new(models.TblTask)
	db.MySql.Table(models.TBNTask).Where("id = ?", obj.Id).First(existed)
	if existed.Id <= 0 {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}

	var clientRoot = saUrl.ConnectUri("http://"+existed.Runner, conf.Conf.Http.ClientRoot)
	var err error

	//停止任务
	if obj.Runner == "" || obj.Status != 2 {
		if existed.Runner != "" && existed.Status == 2 {
			sign, timestamp := genSign()
			_, err = saHttp.PostRequest(
				saUrl.ConnectUri(clientRoot, "task/event"),
				map[string]string{"event": "stop", "key": existed.Key},
				map[string]string{"sign": sign, "timestamp": timestamp},
			)
			if err != nil {
				api.ResError(w, errs.New(err).Error())
				return
			}
		}
	} else {
		//当前实例运行中
		if existed.Runner != "" && existed.Status == 2 {
			//执行实例、执行周期有变化，要先停止当前实例
			if existed.Runner != obj.Runner || existed.Spec != obj.Spec {
				sign, timestamp := genSign()
				_, err = saHttp.PostRequest(
					saUrl.ConnectUri(clientRoot, "task/event"),
					map[string]string{"event": "stop", "key": existed.Key},
					map[string]string{"sign": sign, "timestamp": timestamp},
				)
				if err != nil {
					api.ResError(w, errs.New(err).Error())
					return
				}
			}
		}

		//启动新的实例
		sign, timestamp := genSign()
		_, err = saHttp.PostRequest(
			saUrl.ConnectUri(clientRoot, "task/event"),
			map[string]string{"event": "start", "key": obj.Key, "spec": obj.Spec, "params": obj.Params},
			map[string]string{"sign": sign, "timestamp": timestamp},
		)
		if err != nil {
			api.ResError(w, errs.New(err).Error())
			return
		}
	}

	existed.Name = obj.Name
	existed.Status = obj.Status
	existed.Spec = obj.Spec
	existed.Runner = obj.Runner
	existed.Params = obj.Params
	existed.Remark = obj.Remark
	err = db.MySql.Save(existed).Error
	if err != nil {
		api.ResError(w, errs.New(err).Error())
		return
	}
	api.ResSuccess(w, nil)
}

func TaskRecordsHandler(w http.ResponseWriter, r *http.Request) {
	var query = r.URL.Query()
	var taskId = query.Get("taskId")
	if taskId == "" {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}

	offset, limit := api.Paging(r)
	var cnt int64

	tx := db.MySql.Table(models.TBNTaskLog).Where("task_id = ?", taskId)
	tx.Count(&cnt)
	if cnt <= int64(offset) {
		api.ResSuccess(w, nil, cnt)
		return
	}

	var ary = make([]*models.TblTaskLog, 0, limit)
	err := tx.Offset(offset).Limit(limit).Order("id desc").Find(&ary).Error
	if db.MySql.IsError(err) {
		api.ResError(w, errs.New(err).Error())
		return
	}

	api.ResSuccess(w, ary, cnt)
}

func TaskAddRecordHandler(w http.ResponseWriter, r *http.Request) {
	var params map[string]interface{}
	decoder := json.NewDecoder(r.Body)
	_ = decoder.Decode(&params)

	obj := new(models.TblTaskLog)

	var app = saData.String(params["app"])
	var key = saData.String(params["key"])
	if key == "" || app == "" {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}
	db.MySql.Table(models.TBNTask).Where("app = ?", app).Where("`key` = ?", key).Select("id").Scan(&obj.TaskId)

	start := saTime.TimeFromStr(saData.String(params["start"]), time.DateTime)
	if start.IsZero() {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}
	obj.StartTime = &start
	obj.CreateAt = saTime.Now()
	obj.Runner = saData.String(params["runner"])
	obj.Us, _ = saData.ToInt(params["us"])
	obj.Status = -1
	if success, _ := saData.ToBool(params["success"]); success {
		obj.Status = 2
	}
	obj.Msg = saData.String(params["msg"])

	err := db.MySql.Save(obj).Error
	if err != nil {
		api.ResError(w, errs.New(err).Error())
		return
	}
	api.ResSuccess(w, nil)
}

// TaskRunStatus 查看执行状态
func TaskRunStatus(w http.ResponseWriter, r *http.Request) {
	var query = r.URL.Query()
	var host = query.Get("host")
	var port = query.Get("port")
	var key = query.Get("key")
	if host == "" || port == "" || key == "" {
		api.ResError(w, "host、port、key不可空")
		return
	}

	var clientRoot = saUrl.ConnectUri("http://"+host+":"+port, conf.Conf.Http.ClientRoot)
	sign, timestamp := genSign()
	res, err := saHttp.ToRequest("GET",
		saUrl.ConnectUri(clientRoot, "task/status"),
		map[string]string{"key": key},
		map[string]string{"sign": sign, "timestamp": timestamp},
	)
	if err != nil {
		api.ResError(w, errs.New(err).Error())
		return
	}

	var result = api.Response{
		Code:   0,
		Msg:    "",
		Result: map[string]string{},
	}
	_ = saData.StrToModel(res, &result)
	api.ResSuccess(w, result.Result)
}

// TaskRunHandler start、stop、once
func TaskRunHandler(w http.ResponseWriter, r *http.Request) {
	var params map[string]interface{}
	decoder := json.NewDecoder(r.Body)
	_ = decoder.Decode(&params)

	taskId, _ := saData.ToInt64(params["taskId"])
	var event = params["event"]
	var runner = saData.String(params["runner"])
	var runParams = saData.String(params["params"])
	if taskId <= 0 || event == "" {
		api.ResError(w, errs.ErrorParams.Error())
		return
	}

	var obj = new(models.TblTask)
	err := db.MySql.Table(models.TBNTask).Where("id = ?", taskId).First(obj).Error
	if db.MySql.IsError(err) {
		api.ResError(w, errs.New(err).Error())
		return
	}

	if obj.Id <= 0 {
		api.ResError(w, errs.ErrorNotExisted.Error())
		return
	}

	var taskChanged = false
	var clientRoot = saUrl.ConnectUri("http://"+obj.Runner, conf.Conf.Http.ClientRoot)
	if event == "stop" {
		//先暂停
		if obj.Runner != "" && obj.Status == 2 {
			sign, timestamp := genSign()
			_, err = saHttp.PostRequest(
				saUrl.ConnectUri(clientRoot, "task/event"),
				map[string]string{"event": "stop", "key": obj.Key},
				map[string]string{"sign": sign, "timestamp": timestamp},
			)
			if err != nil {
				api.ResError(w, obj.Runner+"停止任务失败："+err.Error())
				return
			}
		}

		obj.Status = 1
		taskChanged = true
	} else if event == "start" {
		if obj.Status == 2 && obj.Runner == runner {
			api.ResSuccess(w, nil)
			return
		}

		sign, timestamp := genSign()
		_, err = saHttp.PostRequest(
			saUrl.ConnectUri(clientRoot, "task/event"),
			map[string]string{"event": "start", "key": obj.Key, "spec": obj.Spec, "params": runParams},
			map[string]string{"sign": sign, "timestamp": timestamp},
		)
		if err != nil {
			api.ResError(w, obj.Runner+"启动任务失败："+err.Error())
			return
		}

		obj.Runner = runner
		obj.Status = 2
		taskChanged = true
	} else if event == "once" {
		if strings.Contains(","+obj.Registers+",", ","+runner+",") == false {
			api.ResError(w, obj.Runner+"该实例未注册当前服务")
			return
		}

		sign, timestamp := genSign()
		_, err = saHttp.PostRequest(
			saUrl.ConnectUri(clientRoot, "task/event"),
			map[string]string{"event": "once", "key": obj.Key, "params": runParams},
			map[string]string{"sign": sign, "timestamp": timestamp},
		)
		if err != nil {
			api.ResError(w, obj.Runner+"启动任务失败："+err.Error())
			return
		}
	}

	if taskChanged {
		err = db.MySql.Table(models.TBNTask).Save(obj).Error
		if err != nil {
			api.ResError(w, errs.New(err).Error())
			return
		}
	}
	api.ResSuccess(w, nil)
}
